package com.atguigu.exam.service.impl;

import com.atguigu.exam.common.CacheConstants;
import com.atguigu.exam.entity.PaperQuestion;
import com.atguigu.exam.entity.Question;
import com.atguigu.exam.entity.QuestionAnswer;
import com.atguigu.exam.entity.QuestionChoice;
import com.atguigu.exam.mapper.PaperQuestionMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.utils.ExcelUtil;
import com.atguigu.exam.utils.RedisUtils;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.QuestionPageVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;
    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;
    @Autowired
    private PaperQuestionMapper paperQuestionMapper;

    @Override
    public void customPageService(Page<Question> pageBean, QuestionPageVo questionPageVo) {
        questionMapper.customPage(pageBean, questionPageVo);
    }

    @Override
    public Question customDetailQuestion(Long id) {
        //1.查询题目详情
        Question question = questionMapper.customGetById(id);
        if (question == null) {
            throw new RuntimeException("题目查询详情失败！原因可能提前被删除！题目id为：" + id);
        }
        //2.进项热点题目缓存
        new Thread(() -> {
            incrementQuestion(id);
        }).start();
        return question;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customSaveQuestion(Question question) {
        //1.插入题目信息（回显题目id）
        //同一类型不能题目title相同
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Question::getType, question.getType());
        lambdaQueryWrapper.eq(Question::getTitle, question.getTitle());
        boolean exists = baseMapper.exists(lambdaQueryWrapper);
        if (exists) {
            throw new RuntimeException("在%s下，存在%s 名称的题目已经存在！保存失败！".formatted(question.getType(), question.getTitle()));
        }
        boolean save = save(question);
        if (!save) {
            throw new RuntimeException("在%s下，存在%s 名称的题目！保存失败！".formatted(question.getType(), question.getTitle()));
        }
        //2.获取答案对象，并先配置题目id
        QuestionAnswer answer = question.getAnswer();
        answer.setQuestionId(question.getId());
        //3.判断是不是选择题
        if ("CHOICE".equals(question.getType())) {
            //是 -》 循环 -》 选项 + 题目id -> 保存 -》 判断是不是正确 进行 AD
            List<QuestionChoice> choice = question.getChoices();
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < choice.size(); i++) {
                //给每个选项匹配questionId
                // [0 [1] 2 [3] ]
                QuestionChoice questionChoice = choice.get(i);
                questionChoice.setSort(i);
                questionChoice.setQuestionId(question.getId());
                questionChoiceMapper.insert(questionChoice);
                if (questionChoice.getIsCorrect()) {
                    if (stringBuilder.length() > 0) {
                        stringBuilder.append(",");
                    }
                    stringBuilder.append((char) ('A' + i));
                }
            }
            answer.setAnswer(stringBuilder.toString());
        }
        questionAnswerMapper.insert(answer);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customUpdateQuestion(Question question) {
//1.题目的校验
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getTitle, question.getTitle());
        queryWrapper.ne(Question::getId, question.getId());
        boolean exists = baseMapper.exists(queryWrapper);
        if (exists) {
            throw new RuntimeException(("修改：%s题目的新标题：%s和其他的题目重复了！修改失败！".formatted(question.getId(), question.getTitle())));
        }
        //2.修改题目
        boolean b = updateById(question);
        if (!b) {
            throw new RuntimeException("修改：%s题目失败！！".formatted(question.getId()));
        }
        //3.获取答案对象
        QuestionAnswer answer = question.getAnswer();
        //4.判断是否为选择题
        if ("CHOICE".equals(question.getType())) {
            List<QuestionChoice> choices = question.getChoices();
            //删除题目对应的所有选项（原） [根据题目id删除]
            LambdaQueryWrapper<QuestionChoice> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(QuestionChoice::getQuestionId, question.getId());
            questionChoiceMapper.delete(lambdaQueryWrapper);
            //循环新增选项（选项上id == null）
            // 拼接正确的档案 a,b
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < choices.size(); i++) {
                QuestionChoice questionChoice = choices.get(i);
                questionChoice.setSort(i);
                questionChoice.setId(null);
                questionChoice.setCreateTime(null);
                questionChoice.setUpdateTime(null);
                questionChoice.setQuestionId(question.getId());
                questionChoiceMapper.insert(questionChoice);
                if (questionChoice.getIsCorrect()) {
                    if (builder.length() > 0) {
                        builder.append(",");
                    }
                    builder.append((char) ('A' + i));
                }
                //答案对象赋值选择题答案
                answer.setAnswer(builder.toString());
            }
            //5. 进行答案的修改
            questionAnswerMapper.updateById(answer);
            //6. 保证一致性，添加事务
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void customRemoveQuestionById(Long id) {
        //1.判断试卷题目表，存在删除失败
        LambdaQueryWrapper<PaperQuestion> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PaperQuestion::getQuestionId, id);
        Long l = paperQuestionMapper.selectCount(lambdaQueryWrapper);
        if (l > 0) {
            throw new RuntimeException("该题目：%s 被试卷表中引用%s次，删除失败！".formatted(id, l));
        }
        //2.删除主表，题目表
        boolean b = removeById(id);
        if (!b) {
            throw new RuntimeException("该题目：%s 信息删除失败！！");
        }
        //3.删除子表，答案和选项表
        questionAnswerMapper.delete(new LambdaQueryWrapper<QuestionAnswer>().eq(QuestionAnswer::getQuestionId, id));
        questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, id));
    }

    /**
     * 自定义查询热门题目，优先从Redis中获取指定数量的热门题目，
     * 若Redis中数据不足，则从数据库中补充非热门题目，并按创建时间倒序排列。
     *
     * @param size 需要查询的热门题目的数量
     * @return 热门题目列表，包含从Redis和数据库中查询到的题目
     */
    @Override
    public List<Question> customFindPopularQuestions(Integer size) {
        List<Question> popularQuestions = new ArrayList<>();

        // 1. 从Redis中获取热门题目的ID集合（按分数倒序）
        Set<Object> idsSet = redisUtils.zReverseRange(CacheConstants.POPULAR_QUESTIONS_KEY, 0, size - 1);
        if (idsSet != null && idsSet.size() > 0) {
            // 2. 将ID集合转换为Long类型列表，并保持原有顺序
            List<Long> idsList = idsSet.stream().map(s -> Long.valueOf(s.toString())).collect(Collectors.toList());
            log.debug("从redis获取热门题目的id集合，且保证顺序：{}", idsList);
            // 3. 根据ID列表查询题目信息，并保持顺序
            for (Long id : idsList) {
                Question question = getById(id);
                if (question != null) {
                    popularQuestions.add(question);
                }
            }
            log.debug("去redis查询的热门题目，题目数：{},题目内容为：{}", popularQuestions.size(), popularQuestions);
        }

        // 4. 检查是否已经满足所需数量，若不足则从数据库中补充
        int diff = size - popularQuestions.size();
        if (diff > 0) {
            // 5. 查询非热门题目，按创建时间倒序排列，限制数量为差值
            LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.notIn(Question::getId, idsSet);
            lambdaQueryWrapper.orderByDesc(Question::getCreateTime);
            // limit diff;
            lambdaQueryWrapper.last("limit " + diff);
            List<Question> questionDiffList = list(lambdaQueryWrapper);
            log.debug("去question表中补充热门题目，题目数：{},题目内容为：{}", questionDiffList.size(), questionDiffList);
            if (questionDiffList != null && questionDiffList.size() > 0) {
                // 6. 将补充的题目添加到结果集合中
                popularQuestions.addAll(questionDiffList);
            }
        }

        // 7. 填充题目的选项和答案信息
        fillQuestionChoiceAndAnswer(popularQuestions);

        // 8. 返回最终的热门题目列表
        return popularQuestions;
    }

    @Override
    public List<QuestionImportVo> preViewExcel(MultipartFile file) throws Exception {
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("预览数据的文件为空！");
        }
        String filename = file.getOriginalFilename();
        if (!filename.endsWith(".xls") && !filename.endsWith(".xlsx")) {
            throw new RuntimeException("预览数据的文件格式错误，必须是 .xls或者.xlsx！");
        }
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        return questionImportVoList;
    }

    @Override
    public int importBatchQuestions(List<QuestionImportVo> questions) {
        //1. 进行数据校验
        if (questions == null || questions.isEmpty()) {
            throw new RuntimeException("导入题目集合为空");
        }
        int successCount = 0;
        //3. 循环 + try 调用保存的方法 [部分成功]= 0;
        for (int i = 0; i < questions.size(); i++) {
            try {
                //2. 进行vo - question [提取一个方法]
                Question question = convertQuestionImprotVoToQuestion(questions.get(i));
                //数据单体保存
                customSaveQuestion(question);
                //正确计数统计
                successCount++;
            } catch (Exception e) {
                //导入失败提示
                log.debug("{}题目导入失败！", questions.get(i).getTitle());
            }
        }
        return successCount;
    }

    @Override
    public String importExeclBatchQuestions(MultipartFile file) throws Exception {
        //1.校验
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("批量导入的文件为空！");
        }
        String filename = file.getOriginalFilename();
        if (!filename.endsWith(".xls") && !filename.endsWith(".xlsx")) {
            throw new RuntimeException("批量导入的文件格式错误，必须是xls或者xlsx格式！");
        }
        //2.解析文件
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        //3.调用批量导入业务
        int successCount = importBatchQuestions(questionImportVoList);
        String result = "批量导入题目接口调用成功！ 一共：%s 题目需要导入，成功导入了：%s 道题！".formatted(questionImportVoList.size(), successCount);
        return result;
    }

    private Question convertQuestionImprotVoToQuestion(QuestionImportVo questionImportVo) {
        //1.给question本体属性赋值
        Question question = new Question();
        /*
         * 作用：给对象的属性进行赋值！根据另一个对象的相同属性值！
         * 参数1：source 源对象 【提供值】
         * 参数2：target 目标对象 【接收值】
         */
        BeanUtils.copyProperties(questionImportVo, question);
        //2.判断是选择，给选项集合进行赋值
        if ("CHOICE".equals(questionImportVo.getType())) {
            if (questionImportVo.getChoices().size() > 0) {
                List<QuestionChoice> questionChoices = new ArrayList<>(questionImportVo.getChoices().size());
                for (QuestionImportVo.ChoiceImportDto importDto : questionImportVo.getChoices()) {
                    QuestionChoice questionChoice = new QuestionChoice();
                    questionChoice.setContent(importDto.getContent());
                    questionChoice.setIsCorrect(importDto.getIsCorrect());
                    questionChoice.setSort(importDto.getSort());
                    questionChoices.add(questionChoice);
                }
                question.setChoices(questionChoices);
            }
        }
        //3. 不管是不是选择题创建答案对象并赋值 【保存的时候，获取答案对象，选择题可以没有答案值，保存会判断答案值】
        QuestionAnswer questionAnswer = new QuestionAnswer();
        //判断题，需要将true和false转成大写！ 否则无法识别！！
        if ("JUDGE".equals(questionImportVo.getType())) {
            questionAnswer.setAnswer(questionImportVo.getAnswer().toUpperCase());
        } else {
            questionAnswer.setAnswer(questionImportVo.getAnswer());
        }
        questionAnswer.setKeywords(questionImportVo.getKeywords());
        question.setAnswer(questionAnswer);

        return question;
    }


    private void incrementQuestion(Long id) {
        Double score = redisUtils.zIncrementScore(CacheConstants.POPULAR_QUESTIONS_KEY, id, 1);
        log.info("完成{}题目分数累计，累计后分数为：{}", id, score);
    }

    private void fillQuestionChoiceAndAnswer(List<Question> questionList) {
        //1. 非空判断
        if (questionList == null || questionList.size() == 0) {
            log.debug("没有查询对应的问题集合数据！！");
            return;
        }
        //2. 查询所有答案和选项
        //优化查询本次题目的答案和选项
        //查询本地题目集合对应的id集合！！
        List<Long> ids = questionList.stream().map(Question::getId).collect(Collectors.toList());
        //查询本次题目的选项集合
        List<QuestionChoice> questionChoiceList = questionChoiceMapper.selectList(new LambdaQueryWrapper<QuestionChoice>().in(QuestionChoice::getQuestionId, ids));
        //查询本次题目的答案
        List<QuestionAnswer> questionAnswers = questionAnswerMapper.selectList(new LambdaQueryWrapper<QuestionAnswer>().in(QuestionAnswer::getQuestionId, ids));
        //3. 答案和选项进行map转化
        Map<Long, List<QuestionChoice>> questionChoiceMap = questionChoiceList.stream().collect(Collectors.groupingBy(QuestionChoice::getQuestionId));
        Map<Long, QuestionAnswer> answerMap = questionAnswers.stream().collect(Collectors.toMap(QuestionAnswer::getQuestionId, a -> a));
        //4. 循环问题集合，进行选项和答案配置
        questionList.forEach(question -> {
            //每个题目一定答案
            question.setAnswer(answerMap.get(question.getId()));
            //选择题才有选项
            if ("CHOICE".equals(question.getType())) {
                List<QuestionChoice> questionChoices = questionChoiceMap.get(question.getId());
                questionChoices.sort(Comparator.comparingInt(QuestionChoice::getSort));
                question.setChoices(questionChoices);
            }
        });
    }
}